
struct GameModelParameters
{
    //
    // These are variables taht should be initialized before learning
    //
    int historyFrameCount;
    int stateWindowSize;
    int gridScale;
    int maxAbsoluteVariableCount;
    int spriteDatasetSize;
    vector<int> skipIntegers;
    
    //
    // These are variables computed by the learner
    //
    UINT observedIntegerCount;
    UINT totalSpriteCount;
    UINT perFrameVariableCount;
    UINT totalVariableCount;
    Vec2i dimensions;
    vector<int> uniqueSprites;
};

//
// Game model parameters has some dynamic memory so needs a full set of serialization functions
//

inline OutputDataStream& operator << (OutputDataStream &stream, const GameModelParameters &parameters)
{
    stream << parameters.historyFrameCount;
    stream << parameters.stateWindowSize;
    stream << parameters.gridScale;
    stream << parameters.maxAbsoluteVariableCount;
    stream << parameters.spriteDatasetSize;
    stream << parameters.skipIntegers;

    stream << parameters.observedIntegerCount;
    stream << parameters.totalSpriteCount;
    stream << parameters.perFrameVariableCount;
    stream << parameters.totalVariableCount;
    stream << parameters.dimensions;
    stream << parameters.uniqueSprites;
    return stream;
}

inline InputDataStream& operator >> (InputDataStream &stream, GameModelParameters &parameters)
{
    stream >> parameters.historyFrameCount;
    stream >> parameters.stateWindowSize;
    stream >> parameters.gridScale;
    stream >> parameters.maxAbsoluteVariableCount;
    stream >> parameters.spriteDatasetSize;
    stream >> parameters.skipIntegers;

    stream >> parameters.observedIntegerCount;
    stream >> parameters.totalSpriteCount;
    stream >> parameters.perFrameVariableCount;
    stream >> parameters.totalVariableCount;
    stream >> parameters.dimensions;
    stream >> parameters.uniqueSprites;
    return stream;
}

struct GameModelFrame
{
    GameModelFrame(const Frame *frame)
    {
        sourceFrame = frame;
        cachedFeatures = NULL;
    }

    ~GameModelFrame()
    {
        if(cachedFeatures) delete cachedFeatures;
    }

    const SpriteLocation* FindUniqueSprite(int targetSpriteID) const
    {
        for(UINT spriteIndex = 0; spriteIndex < sourceFrame->sprites.size(); spriteIndex++)
        {
            if(sourceFrame->sprites[spriteIndex].spriteID == targetSpriteID)
            {
                return &(sourceFrame->sprites[spriteIndex]);
            }
        }
        return NULL;
    }

    const SpriteLocation* SpriteAtLocation(const Vec2i &coord) const
    {
        for(UINT spriteIndex = 0; spriteIndex < sourceFrame->sprites.size(); spriteIndex++)
        {
            if(sourceFrame->sprites[spriteIndex].coord == coord)
            {
                return &sourceFrame->sprites[spriteIndex];
            }
        }
        return NULL;
    }

    bool SpriteAtLocation(const Vec2i &coord, int spriteID) const
    {
        const SpriteLocation* result = SpriteAtLocation(coord);
        if(result == NULL) return false;
        return (result->spriteID == spriteID);
    }

    void RecordSpriteWindow(const GameModelParameters &parameters, int x, int y, short *features, int &featureIndex) const;
    void MakeFeatureVector(const GameModelParameters &parameters, int x, int y, short *features);
    void OverwriteDynamicFeatures(const GameModelParameters &parameters, int x, int y, short *features, bool copyFromCache);
    void ComputeSpriteCounts(const GameModelParameters &parameters);

    const Frame *sourceFrame;
    vector<GameModelFrame*> historyFrames;
    vector<int> spriteCount;
    short *cachedFeatures;
};
